#ifndef _COMPLEX_OPS_H_
#define _COMPLEX_OPS_H_

#include <complex>
#include <functional>
#include <cmath>

using namespace std;

template
<
    typename Arg,
    typename Result
>
struct absolute : unary_function <Arg, Result>
{
    Result operator() (const Arg& arg) const
    {
        return abs<Result>(arg);
    }
};

template
<
    typename Exp,
    typename Result
>
struct power : binary_function <Result, Exp, Result>
{
    Result operator() (const Result& arg1, const Exp& arg2) const
    {
        return pow(arg1, arg2);
    }
};

template
<
    typename Exp,
    typename T
>
struct power <Exp, complex<T> > : binary_function <complex<T> , Exp, complex<T>>
{
    complex<T>  operator() (const complex<T> & arg1, const Exp& arg2) const
    {
        return pow<T>(arg1, arg2);
    }
};

struct plus_random : binary_function <std::complex<double>, double, std::complex<double>>
{
    complex<double> operator() (const std::complex<double>& arg, double snr) const
    {
        double x = rand();
        x /= (double)RAND_MAX;
        double y = rand();
        y /= (double)RAND_MAX;
        //cout << "xxx = " << snr << endl;
        return arg + (std::complex<double>(x - .5, y - .5) / snr) ;
    }
};

template
<
    typename Radius
>
std::complex<double> randomValue()
{
    double radius = Radius().value;
    double x = rand();
    x /= (double)RAND_MAX;
    double y = rand();
    y /= (double)RAND_MAX;
        
    return 2 * radius * std::complex<double>(x - .5, y - .5);
}

double sumnorms(double sum, const complex<double>& z)
{
    using namespace std;
    double r = abs(z);
    return sum + r * r;
}

template
<
    typename Signal
>
double energy(const Signal& signal)
{
    return accumulate(
            signal.begin(), signal.end(),
            (double)0.0, sumnorms
    );
}

#endif // _COMPLEX_OPS_H_
